#ifndef TYPES_H
#define TYPES_H
#include <string>
#include <vector>
#include <string.h>
using namespace std;

template<class T>
void ValueToBuffer(T src, uint8_t **dst)
{
    memcpy(*dst, &src, sizeof(T));
    *dst = (uint8_t*)(((T*)*dst) + 1);
}

template<class T>
void ValueFromBuffer(uint8_t **src, T &dst)
{
    memcpy(&dst, *src, sizeof(T));
    *src = (uint8_t*)(((T*)*src) + 1);
}

template<class T>
void VectorToBuffer(vector<T> src, uint8_t **dst)
   {
    uint32_t vectorSize = static_cast<uint32_t>(src.size());
    memcpy(*dst, &vectorSize, sizeof(int32_t));
    *dst = *dst + sizeof(uint32_t);

    for (uint32_t n = 0; n < src.size(); n++) {
        int32_t index = n * sizeof(T);
        T item = src[n];
        memcpy(*dst + index, &item, sizeof(T));
    }
    *dst = (uint8_t*)(((T*)*dst) + src.size());
}


template<class T>
void VectorFromBuffer(uint8_t **src, vector<T> &dst)
    {
    uint32_t length = 0;
    memcpy(&length, *src, sizeof(int32_t));
    *src = *src + sizeof(uint32_t);

    dst.clear();
    dst.reserve(length);
    for (uint32_t n = 0; n < length; n++) {
        int32_t index = n * sizeof(T);
        T item;
        memcpy(&item, *src + index, sizeof(T));
        dst.push_back(item);
    }
    *src = (uint8_t*)(((T*)*src) + length);
}


template<class T>
void VectorOfVectorsToBuffer(vector< vector<T> > src, uint8_t **dst)
{
    uint32_t numVectors = src.size();
    ValueToBuffer(numVectors, dst);
    for (uint32_t i = 0; i < src.size(); i++)
    {
        VectorToBuffer<T>(src[i], dst);
    }
}


template<class T>
void VectorOfVectorsFromBuffer(uint8_t **src, vector< vector<T> >& dst)
{
    uint32_t size = 0;
    ValueFromBuffer(src, size);
    for (uint32_t i = 0; i < size; i++)
    {
        vector<T> vs12;
        VectorFromBuffer<T>(src, vs12);
        (dst).push_back(vs12);
    }
}


template<class T, class T2>
void StringToBuffer(T src, uint8_t **dst) {
    uint32_t length = static_cast<uint32_t>(src.size());
    uint32_t lengthInBytes = length * sizeof(T2);
    memcpy(*dst, &lengthInBytes, sizeof(uint32_t));
    *dst = *dst + sizeof(uint32_t);
    memcpy(*dst, src.c_str(), lengthInBytes);

    *dst = *dst + lengthInBytes;
}

template<class T, class T2>
void StringFromBuffer(uint8_t **src, T& dst) {
    uint32_t lengthInBytes = 0;
    memcpy(&lengthInBytes, *src, sizeof(uint32_t));
    *src = *src  + sizeof(uint32_t);
    uint32_t length = lengthInBytes / sizeof(T2);
    dst.assign((T2*)*src, length);
    *src = (*src + lengthInBytes);
}

template<class T, class T2>
void VectorOfStringsToBuffer(vector<T> src, uint8_t **dst)
{
    uint32_t numStrings = static_cast<uint32_t>(src.size());
    ValueToBuffer(numStrings, dst);
    for (uint32_t i = 0; i < static_cast<uint32_t>(src.size()); i++)
    {
        StringToBuffer<T, T2>(src[i], dst);
    }
}

template<class T, class T2>
void VectorOfStringsFromBuffer(uint8_t **src, vector<T>& dst)
{
    uint32_t size = 0;
    ValueFromBuffer(src, size);
    for (uint32_t i = 0; i < size; i++)
    {
        T vs12;
        StringFromBuffer<T, T2>(src, vs12);
        (dst).push_back(vs12);
    }
}

struct MsgHead
{
    uint8_t  src;
    uint8_t  dest;
    uint8_t  msgid;
    uint16_t msglen;
    MsgHead()
    {
        src    = 0;
        dest   = 0;
        msgid  = 0;
        msglen = 0;
    }
};

struct LoginData
{
    string username;
    string password;
    LoginData()
    {
        username = "";
        password = "";
    }

    LoginData& operator=(const LoginData& data)
    {
        username = data.username;
        password = data.password;
        return *this;
    }
    LoginData (const LoginData& data)
    {
        username = data.username;
        password = data.password;
    }
};

struct InfoData
{
    uint32_t height;
    uint32_t weight;
    uint8_t  age;
    bool     married;
    InfoData()
    {
        height  = 0;
        weight  = 0;
        age     = 0;
        married = false;
    }
    InfoData& operator=(const InfoData& data)
    {
        height  = data.height;
        weight  = data.weight;
        age     = data.age;
        married = data.married;
        return *this;
    }
    InfoData (const InfoData& data)
    {
        height  = data.height;
        weight  = data.weight;
        age     = data.age;
        married = data.married;
    }
};

#endif // TYPES_H
